package org.example.azapi;

import java.util.Set;

import org.example.azapi.AzEntity;
import org.example.azapi.constants.AzCategoryId;
import org.example.azapi.constants.AzCategoryIdAction;
import org.example.azapi.constants.AzCategoryIdEnvironment;
import org.example.azapi.constants.AzCategoryIdResource;
import org.example.azapi.constants.AzCategoryIdSubjectAccess;
import org.example.azapi.constants.AzCategoryIdSubjectCodebase;
import org.example.azapi.constants.AzCategoryIdSubjectIntermediary;
import org.example.azapi.constants.AzCategoryIdSubjectRecipient;
import org.example.azapi.constants.AzCategoryIdSubjectRequestingMachine;


/**
 * The AzRequestContext is the structure used to collect all the 
 * information that is required to make one or more authorization 
 * requests.
 * <p>
 * The AzRequestContext is used as the input to a decision. 
 * It primarily consists of a group of collections of AzEntity
 * objects, where each AzEntity contains a collection of 
 * AzAttributes describing that entity. From a XACML perspective,
 * one may think of the AzEntity objects representing "actors" in
 * a XACML decision request, where the actors consist of the entity
 * making the request (Subject), the entity representing the request
 * (Action), the entity representing that which access is being requested
 * to (Resource) and an entity capturing general operating conditions
 * under which the request is being made (Environment).
 * one or more AzSubjects.
 * <p>
 * The official term XACML uses to distinguish these actors is "Category".
 * The allowed Category identifiers are represented in AzApi by Enums that
 * implement the {@link org.example.azapi.constants.AzCategoryId} interface.
 * The general rule is that a single authorization decision corresponds to
 * a set of entities, where only one entity per Category is allowed. 
 * <p>
 * The general mechanism by which multiple decisions can be requested in a
 * single AzRequestContext object is by including multiple entities within
 * the same category, then for each entity within a category, a separate 
 * decision will be made using each member of the specific category, against
 * a fixed set of members, one each, of the other categories. The specifics
 * of these mechanisms vary, with the most significant constraint being
 * that AzResourceActionAssociations are created to indicate specific
 * resource-action combinations against which a decision will be requested
 * as opposed to simply requesting the cross-product of all resources and
 * actions in an AzRequestContext.
 * <p>
 * Note:  an AzRequestContext may be populated incrementally by 
 * several independent software components prior to the request 
 * actually being submitted to a PDP.
 * <br>
 * Note: there is no direct support for XACML Attribute Selectors,
 * which are primarily xpaths used to access attributes in supplied
 * xml content. All attributes from this AzAPI should be considered
 * to be accessible using XACML Attribute Designators, which are based
 * on using reference to the AttributeId(required), Issuer(optional), 
 * DataType(required), and Category(required, but generally implicit)
 * associated with a XACML Attribute.
 * @author Rich
 *
 */
public interface AzRequestContext {
	
	/**
	 * Creates and returns an AzEntity<AzCategoryIdResource> object.
	 * <p> 
	 * The created AzEntity<AzCategoryIdResource> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzResource" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdResource>
	 * <p>
	 * Note: adding an AzEntity<AzCategoryIdResource> object to internal
	 * collection does not guarantee the Resource will be used in a decision.
	 * In order to make that guarantee, the Resource needs to be added
	 * to an AzResourceActionAssociation object.
	 * @param t an AzCategoryIdResource Enum
	 * @return a new AzEntity<AzCategoryIdResource> object that is also added
	 * to the internal collection.
	 * 
	 */
	public AzEntity<AzCategoryIdResource> 
				createAzEntity(AzCategoryIdResource t);

	/**
	 * Creates and returns an AzEntity<AzCategoryIdAction> object.
	 * <p> 
	 * The created AzEntity<AzCategoryIdAction> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzAction" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdAction>
	 * <p>
	 * Note: adding an AzEntity<AzCategoryIdAction> object to internal
	 * collection does not guarantee the Action will be used in a decision.
	 * In order to make that guarantee, the Action needs to be added
	 * to an AzResourceActionAssociation object.
	 * @param t an AzCategoryIdAction Enum
	 * @return a new AzEntity<AzCategoryIdAction> object that is also added
	 * to the internal collection.
	 * 
	 */
	public AzEntity<AzCategoryIdAction> 
				createAzEntity(AzCategoryIdAction t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdEnvironment> object.
	 * The created AzEntity<AzCategoryIdEnvironment> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzEnvironment" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdEnvironment>
	 * <p>
	 * TBD: will multiple AzEntity<AzCategoryIdEnvironment> objects
	 * be allowed, and, if so, what rules will apply in terms of
	 * determining what the requested set of decision requests
	 * will be based on presence of multiple instances of these
	 * type os objects.
	 * <p>
	 * @param an AzCategoryIdEnvironment Enum
	 * @return a reference to an azEntity<AzCategoryIdEnvironment> object
	 */
	public AzEntity<AzCategoryIdEnvironment> 
				createAzEntity(AzCategoryIdEnvironment t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdSubjectAccess> object.
	 * The created AzEntity<AzCategoryIdSubjectAccess> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzSubject" or "AzSubjectAccess" may be used 
	 * in discussion as a synonym for 
	 * AzEntity<AzCategoryIdSubjectAccess>
	 * <p>
	 * TBD: will multiple AzEntity<AzCategoryIdSubjectAccess> objects
	 * be allowed, and, if so, what rules will apply in terms of
	 * determining what the requested set of decision requests
	 * will be based on presence of multiple instances of these
	 * type of objects. This TBD applies to all the Subject* AzEntity
	 * objects.
	 * <p>
	 * @param an AzCategoryIdSubjectAccess Enum
	 * @return a reference to an azEntity<AzCategoryIdSubjectAccess> 
	 * object
	 */
	public AzEntity<AzCategoryIdSubjectAccess> 
				createAzEntity(AzCategoryIdSubjectAccess t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdSubjectCodebase> object.
	 * The created AzEntity<AzCategoryIdSubjectCodebase> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzSubjectCodebase" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdSubjectCodebase>
	 * <p>
	 * @param an AzCategoryIdSubjectCodebase Enum
	 * @return a reference to an azEntity<AzCategoryIdSubjectCodebase> 
	 * object
	 */
	public AzEntity<AzCategoryIdSubjectCodebase> 
				createAzEntity(AzCategoryIdSubjectCodebase t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdSubjectIntermediary> object.
	 * The created AzEntity<AzCategoryIdSubjectIntermediary> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzSubjectIntermediary" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdSubjectIntermediary>
	 * <p>
	 * @param an AzCategoryIdSubjectIntermediary Enum
	 * @return a reference to an azEntity<AzCategoryIdSubjectIntermediary> 
	 * object
	 */
	public AzEntity<AzCategoryIdSubjectIntermediary> 
				createAzEntity(AzCategoryIdSubjectIntermediary t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdSubjectRecipient> object.
	 * The created AzEntity<AzCategoryIdSubjectRecipient> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzSubjectRecipient" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdSubjectRecipient>
	 * <p>
	 * @param an AzCategoryIdSubjectRecipient Enum
	 * @return a reference to an azEntity<AzCategoryIdSubjectRecipient> 
	 * object
	 */
	public AzEntity<AzCategoryIdSubjectRecipient> 
				createAzEntity(AzCategoryIdSubjectRecipient t);

	/*
	 * Creates and returns an AzEntity<AzCategoryIdSubjectRequestingMachine> object.
	 * The created AzEntity<AzCategoryIdSubjectRequestingMachine> entity is added to the
	 * internal collection.
	 * <p>
	 * Note: The term "AzSubjectRequestingMachine" may be used in discussion as a
	 * synonym for AzEntity<AzCategoryIdSubjectRequestingMachine>
	 * <p>
	 * @param an AzCategoryIdSubjectRequestingMachine Enum
	 * @return a reference to an azEntity<AzCategoryIdSubjectRequestingMachine> 
	 * object
	 */
	public AzEntity<AzCategoryIdSubjectRequestingMachine> 
				createAzEntity(AzCategoryIdSubjectRequestingMachine t);

	//public AzEntity<AzCategoryIdAction> createAzEntity(AzCategoryIdAction t);
	//public AzEntity<AzCategoryIdObligation> createAzEntity(AzCategoryIdObligation t);
	
	/**
	 * Adds an AzEntity<T> object to the request context, subject
	 * to the restrictions on the number of AzEntities of that type
	 * allowed.
	 * @param <T>
	 * @param azEntity
	 * @return boolean true if add was successful, otherwise false
	 */
	public <T extends Enum<T> & AzCategoryId> 
		boolean addAzEntity(AzEntity<T> azEntity);

	//public <T extends Enum<T> & AzCategoryId> 
	//	AzEntity<T> getAzEntity(T t);
	public AzEntity<AzCategoryIdAction> getAzEntity(AzCategoryIdAction t);
	public AzEntity<AzCategoryIdEnvironment> getAzEntity(AzCategoryIdEnvironment t);
	//public AzEntity<AzCategoryIdObligation> createAzEntity(AzCategoryIdObligation t);
	public AzEntity<AzCategoryIdResource> getAzEntity(AzCategoryIdResource t);
	public AzEntity<AzCategoryIdSubjectAccess> getAzEntity(AzCategoryIdSubjectAccess t);
	public AzEntity<AzCategoryIdSubjectCodebase> getAzEntity(AzCategoryIdSubjectCodebase t);
	public AzEntity<AzCategoryIdSubjectIntermediary> getAzEntity(AzCategoryIdSubjectIntermediary t);
	public AzEntity<AzCategoryIdSubjectRecipient> getAzEntity(AzCategoryIdSubjectRecipient t);
	public AzEntity<AzCategoryIdSubjectRequestingMachine> getAzEntity(AzCategoryIdSubjectRequestingMachine t);
	//public AzEntity<AzCategoryIdAction> createAzEntity(AzCategoryIdAction t);

	/**
	 * Returns an AzAction object which is intended to be used to
	 * hold a collection of AzActionAttributes. The AzActionAttributes
	 * that the AzAPI caller will supply describe the Action that is 
	 * intended to be performed by the Subject on the Resource. 
	 * i.e. AzAction contains the "action portion" of the authorization 
	 * request.
	 * @return an azAction container for AzActionAttributes
	 * @see AzAction
	 */
	//public AzAction createAzAction();
	
	/**
	 * Returns an AzEnvironment container for a collection of 
	 * AzEnvironmentAttributes.
	 * @return an azEnvironment container for AzEnvironmentAttributes
	 * @see AzEnvironment
	 */
	//public AzEnvironment createAzEnvironment();
	
	/**
	 * Returns an AzResource collection container for 
	 * AzResourceAttributes.
	 * @return AzResource
	 * @see AzResource
	 */
	//public AzResource createAzResource();
	
	/**
	 * Returns an AzSubject collection container for AzSubjectAttributes
	 * w default SubjectCategory = AzSubjectCategoryId.X_CATEGORY_SUBJECT_ACCESS = 
	 * urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
	 * 
	 * @return AzSubject
	 * @see AzSubject
	 * @see AzSubjectCategoryId#X_CATEGORY_SUBJECT_ACCESS
	 */
	//public AzSubject createAzSubject();

	/**
	 * Returns an AzSubject collection container for AzSubjectAttributes.
	 * The returned AzSubject object will be preset with the 
	 * AzSubjectCategoryId provided as a parameter, such that all 
	 * AzSubjectAttributes that are later added by the AzAPI caller
	 * will apply to the Subject having the specified 
	 * AzSubjectCategoryId
	 * @param AzSubjectCategoryId
	 * @return AzSubject
	 * @see AzSubject
	 * @see AzSubjectCategoryId
	 */
	//public AzSubject createAzSubject(AzSubjectCategoryId c);

	/** 
	 * Add a new or replace the existing AzEnvironment entity in the
	 * AzRequestContext. 
	 * <p>
	 * Only one AzEnviroment entity is allowed to be present.
	 * <p>
	 * Adding a null AzEnvironment object will effectively remove any
	 * existing AzEnvironment object and an empty Xacml Environment 
	 * element will be generated in an authorization request.
	 * @param environment
	 */
	//public void addAzEnvironment(AzEnvironment azEnvironment);
	
	/**
	 * Add or replace an AzSubject entity, which will be of one of the
	 * allowed Subject categories specified by AzSubjectCategoryId,
	 * to the AzRequestContext.
	 * <p>
	 * There is only one AzSubject allowed in the AzRequestContext with
	 * a specific AzSubjectCategoryId. Multiple Subjects are allowed if
	 * they have different AzSubjectCategoryIds.
	 * <p>
	 * Note: the AzRequestContext will always provide an empty XACML
	 * Subject element if no AzSubjects have been added or if all 
	 * AzSubjects that had been added have been removed.
	 * <p>   
	 * There are no specific Attributes required to be present in the
	 * AzSubject entity. XACML decisions are based on information
	 * provided and the Policies and Rules that are determined to be
	 * applicable based on whatever information or lack of information
	 * is included in the Request.
	 * <p>
	 * @param azSubject
	 */
	//public void addSubject(AzSubject azSubject);
	
	/**
	 * Removes the AzSubject, if one exists, with the specified AzCategoryId.
	 * If no AzSubjects remain, AzRequestContext provides empty Subject element 
	 * for input. i.e. Subject element will always be provided, even if empty.
	 * 
	 * @param azSubjectCategoryId
	 * @return boolean true if object found and removed, ow false
	 */
	//public boolean removeSubject(AzSubjectCategoryId azSubjectCategoryId);
	
	/**
	 * Add an AzResourceActionAssociation to the AzRequestContext by providing
	 * an AzResource entity and an AzAction entity, which the method will 
	 * combine into the appropriate association object and add it to the Set
	 * of associations.
	 * <p>
	 * Note that the association is based on associating the AzResource and
	 * AzAction objects, independent of the sets of attributes that the 
	 * objects contain. 
	 * <br>
	 * Therefore, the association can be established independently of the 
	 * presence of resource-id and action-id attributes within the objects.
	 * <p>
	 * TBD: do we need to require the presence of resource-id and action-id
	 * attributes and thence a relate Exception? These are not required by 
	 * XACML 2.0 (for resource-id see sections 6.3 (MAY contain one or more 
	 * resource-id attributes), section 6.10 (lines 3080-3083), section B.6 )
	 * <br> 
	 * (for action-id see section B.7, section 6.1 (lines 2896-2898), 
	 * section 6.5
	 * @param azResource
	 * @param azAction
	 * @return a reference to the newly created azResourceActionAssociation,
	 * 			which may be used to correlate with a specific returned
	 * 			azResult.
	 * @see AzResourceActionAssociation
	 * @see AzResult
	 */
	public AzResourceActionAssociation createAndAddResourceActionAssociation(
			AzEntity<AzCategoryIdResource> azResource, 
			AzEntity<AzCategoryIdAction> azAction);

	/**
	 * Return the set of tuples corresponding to the resource and 
	 * action pairs that have been set in the resource context.
	 * @return the Set<AzResourceActionAssociation> currently active
	 * for this AzRequestContext
	 */
	public Set<AzResourceActionAssociation> getAssociations();	

	/**
	 * Add an AzResourceActionAssociation to the AzRequestContext for each
	 * possible AzResource-AzAction combination (cross-product (nxm)) of a 
	 * Set of "n" AzResource entities and a Set of "m" AzAction entities.
	 * <br>   
	 * Note that as in the single entity parameters version of this method,
	 * that all the associations are established based on object pairs. This
	 * means that if multiple AzResource objects have the same resource-id 
	 * attribute, that they will be treated as distinct resources for the
	 * purpose of making decisions.
	 * <br>
	 * Note also, that if another addResourceActionAssociation call is made
	 * with some of the same AzResource and AzAction objects, that any 
	 * identical AzResource-AzAction pairs will degenerate to a single
	 * association. In general, after all the addResourceActionAssociation
	 * calls have been made there is conceptually an NxM matrix of 
	 * N AzResources and M AzActions, where N is the sum of all the 
	 * distinct AzResources in the individual calls and similarly for
	 * the M distinct AzActions. 
	 * <p>
	 * For concreteness consider N AzResource rows and M AzAction columns,
	 * where a single AzResource is a single row and each cell in that row
	 * represents one of the M actions that potentially could be performed
	 * on that resource.
	 * <p> 
	 * Similarly the removeResourceActionAssociation calls will remove
	 * any resource-action object pairs that have been added that are
	 * identical to any of the resource-action object pairs in the
	 * current remove call.
	 * <br>
	 * At the end of all the add and remove calls there will remain N'
	 * AzResource objects and M' AzAction objects participating in a 
	 * maximum of N'xM' associations or a minimum of the larger of (N',M')
	 * associations, or any combination in between.
	 * <br> 
	 * <p>
	 * TBD: there has been some discussion that if the resource-id and
	 * action-id could be guaranteed to be present, singular,  and unique 
	 * in every AzResource and AzAction object, then resource-id<->action-id
	 * pairings would effectively be isomorphic (the same) as the pairings
	 * of the objects described above.
	 * <br> some of the considerations for not doing things this way include
	 * <br> that it is not required by XACML 2.0 or 3.0  
	 * <br>that it is not possible to anticipate in advance the how or why 
	 * that users will want to batch the requests, 
	 * <br> that there is the possibility of two requests having AzResource 
	 * entities for the same resource-id (or action-id), but with a different
	 * set of attributes,
	 * <br> and finally, it would seem a fairly straight-forward extension
	 * to this conceptualization to subclass the AzResource and AzAction
	 * entities and the AzRequestContext to apply filtering to achieve 
	 * this effect.
	 * <p>
	 * Note: use of a MissingRequestIdException/MissingActionIdException,
	 * might be the mechanism to signal this condition, if necessary.
	 * @param azResourceSet
	 * @param azActionSet
	 * @return the Set<AzResourceActionAssociation> objects that were
	 * just added to the collection
	 * @see #createAndAddResourceActionAssociation(AzEntity, AzEntity)
	 */
	public Set<AzResourceActionAssociation> addResourceActionAssociation(
			Set<AzEntity<AzCategoryIdResource>> azResourceSet, 
			Set<AzEntity<AzCategoryIdAction>> azActionSet);
	
	/**
	 * Add the cross-product of a single AzResource and a Set of AzActions 
	 * to the AzRequestContext.
	 * @param resource
	 * @param actions
	 * @return the Set<AzResourceActionAssociation> objects that were
	 * just added to the collection
	 */
	public Set<AzResourceActionAssociation> addResourceActionAssociation(
			AzEntity<AzCategoryIdResource> resource, 
			Set<AzEntity<AzCategoryIdAction>> actions);
	
	/**
	 * Add the cross-product of a Set of AzResources and a single AzAction 
	 * to the AzRequestContext.
	 * @param resources
	 * @param action
	 * @return a reference to the Set<AzResourceActionAssociation> just
	 * added to AzRequestContext
	 */
	public Set<AzResourceActionAssociation> addResourceActionAssociation(
			Set<AzEntity<AzCategoryIdResource>> resources, 
			AzEntity<AzCategoryIdAction> action);
	
	/**
	 * Delete the AzResource and AzAction association from the AzRequestContext 
	 * collection of AzResourceActionAssociations.
	 * <p>
	 * It is only the association that is deleted. If the AzResource entity 
	 * and/or the AzAction entity belong to other AzResourceActionAssociations,
	 * then the AzResource and/or AzAction object will be kept in its 
	 * respective collection. If an AzAction or AzResource object entity no 
	 * longer is part of any association, then no reference to the object
	 * will be kept by the AzRequestContext and it will be considered deleted
	 * from the AzRequestContext.  
	 * @param resource
	 * @param action
	 */
	public void removeResourceActionAssociation(
			AzEntity<AzCategoryIdResource > resource, 
			AzEntity<AzCategoryIdAction> action);
	
	/**
	 * delete the cross-product of the AzResource and AzAction sets from 
	 * the AzRequestContext, should the association exist. 
	 * @param resources
	 * @param actions
	 */
	public void removeResourceActionAssociations(
			Set<AzEntity<AzCategoryIdResource>> resources, 
			Set<AzEntity<AzCategoryIdAction>> actions);
	
	/**
	 * Remove all AzResourceActionAssociations from AzRequestContext.
	 * Helper method to safely reset collection of AzResourceActionAssociations.
	 */
	public void removeAllResourceActionAssociations();


	/**
	 * Return the AzEnvironment component of the AzRequestContext 
	 * or null if it has not been added. 
	 * <p>
	 * If null, then empty 
	 * XACML Environment element will be sent when 
	 * AzService.decide() call is issued.
	 * TBD: determine whether to support multiple AzEnvironment entities
	 * @return the AzEntity<AzCategoryIdEnvironment> object
	 */
	public AzEntity<AzCategoryIdEnvironment> getAzEnvironment();
	
	/**
	 * Consider making this static to see if we can return explicit
	 * instances of specific subtypes of AzEntity<T>.
	 * @param <T>
	 * @param t
	 * @return
	 */
	//public static <T extends Enum<T> & AzCategoryId>
	//	AzEntity<T> getAzEntity(T t);

	/**
	 * Get the Set of AzEntity<AzCategoryIdResource>'s held by this 
	 * AzRequestContext. 
	 * <p>
	 * If no AzResources have been added an empty Set will be returned.
	 * <p>
	 * Only AzResources that have been identified by 
	 * AzResourceActionAssociations will be included in authorization
	 * requests.
	 * @return the Set<AzEntity<AzCategoryIdResource>> that have
	 * been added to this AzRequestContext
	 */
	public Set<AzEntity<AzCategoryIdResource>> getResources();

	/**
	 * Get the Set<AzEntity<AzCategoryIdAction>> held by this 
	 * AzRequestContext. 
	 * <p>
	 * If no Action entities have been added an empty Set will be returned.
	 * <p>
	 * Note: This Set may contain AzEntity<AzCategoryIdAction> objects
	 * that will not be included in decisions, unless they have been
	 * identified in the set of AzResourceActionAssociations that 
	 * control for which resource-action pairs decisions are being
	 * requested.
	 * 
	 * @return a Set<AzEntity<AzCategoryIdAction>> containing the
	 * actions that have been provided.
	 */
	public Set<AzEntity<AzCategoryIdAction>> getActions();

	/**
	 * Get all the values corresponding to the distinguished 
	 * AttributeId resource-id found in each Resource
	 * <p>
	 * Only AzActions that have been identified by 
	 * AzResourceActionAssociations will be included in authorization
	 * requests.
	 * @return
	 */
	//public Set<String> getResourceIds();

	/**
	 * Get all the values corresponding to the distinguished 
	 * AttributeId action-id found in each Action
	 * @return
	 */
	//public Set<String> getActionIds();

}
